package com.t.bricks.bussiness.component.systemlog;

import java.lang.reflect.Field;
import java.util.Date;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.t.bricks.annotation.DataInfo;
import com.t.bricks.bussiness.db.entity.systemlog.SystemLog;
import com.t.bricks.bussiness.db.mapper.systemlog.SystemLogMapper;
import com.t.bricks.model.Log;
import com.t.bricks.model.MsgEmity;
import com.t.bricks.utils.spring.ModuleUtil;

/**
 * 用切面方式监听各service的数据操作
 */
@Aspect
@Component
public class SystemLogAspect {

	/**
	 * 系统日志业务实现类
	 */
	@Autowired
	private SystemLogMapper systemLogMapper;
	
	/**
	 * 定义切入点，切入点为'MsgEmity com.t.bricks.bussiness.db.dao.*.impl.*DaoImpl'下的add函数,并且函数可以是任意多的参数(用?会异常),但排除SystemLogService本身
	 * 1、execution(public * *(..)) 任意的公共方法
	 * 2、execution(* set*(..)) 以set开头的所有的方法
	 * 3、execution(* com.lingyejun.annotation.LoggerApply.*(..))com.lingyejun.annotation.LoggerApply这个类里的所有的方法
	 * 4、execution(* com.lingyejun.annotation.*.*(..))com.lingyejun.annotation包下的所有的类的所有的方法
	 * 5、execution(* com.lingyejun.annotation..*.*(..))com.lingyejun.annotation包及子包下所有的类的所有的方法
	 * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数，第一个参数为String类型，第二个参数为任意类型，第三个参数为Long类型的方法
	 * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
	 */
	@Pointcut(value = "execution(* com.t.bricks.bussiness.service.*.impl..*ServiceImpl.add(..)) && !execution(* com.t.bricks.bussiness.service.systemlog..*.*(..)) ")
	public void addMethod() {
	}

	/**
	 * 定义切入点，切入点为'MsgEmity com.t.bricks.bussiness.db.dao.*.impl.*DaoImpl'下的del函数,并且函数可以是任意多的参数(用?会异常),但排除SystemLogService本身
	 */
	@Pointcut(value = "execution(* com.t.bricks.bussiness.service.*.impl..*ServiceImpl.del(..)) && !execution(* com.t.bricks.bussiness.service.systemlog..*.*(..)) ")
	public void delMethod() {
	}

	/**
	 * 定义切入点，切入点为'MsgEmity com.t.bricks.bussiness.db.dao.*.impl.*DaoImpl.edit(..)'下的edit函数,并且函数可以是任意多的参数(用?会异常),但排除SystemLogService本身
	 */
	@Pointcut(value = "execution(* com.t.bricks.bussiness.service.*.impl..*ServiceImpl.edit(..))) && !execution(* com.t.bricks.bussiness.service.systemlog..*.*(..))")
	public void editMethod() {
	}

	/**
	 * 后置/返回值增强
     * @param joinPoint
     * @param result 被切点方法执行后的返回值
     * @return
	 */
	@AfterReturning(returning="result", pointcut="addMethod()")
	public void afterReturningByAdd(JoinPoint joinPoint, Object result) {
		afterReturning(joinPoint, result, "新增");
	}

	/**
	 * 后置/返回值增强
     * @param joinPoint
     * @param result 被切点方法执行后的返回值
     * @return
	 */
	@AfterReturning(returning="result", pointcut="delMethod()")
	public void afterReturningByDel(JoinPoint joinPoint, Object result) {
		afterReturning(joinPoint, result, "删除");
	}

	/**
	 * 后置/返回值增强
     * @param joinPoint
     * @param result 被切点方法执行后的返回值
     * @return
	 */
	@AfterReturning(returning="result", pointcut="editMethod()")
	public void afterReturningByEdit(JoinPoint joinPoint, Object result) {
		afterReturning(joinPoint, result, "修改");
	}
	
	/**
	 * 抽象的方法
	 * @param joinPoint
	 * @param result
	 * @param sType
	 */
	private void afterReturning(JoinPoint joinPoint, Object result, String sType) {
		if (!(result instanceof MsgEmity)) {//不是返回MsgEmity,则说明不是要进行的
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			String methodName = signature.getMethod().getName();
			Log.error("方法'", methodName, "'返回数据不是MsgEmity");
			return;
		}
		
		//String currentLoginUserIdtest = ModuleUtil.currentLoginUserId();//取当前登录用户//此方法无法获取,留此代码作为标记

		MsgEmity me = (MsgEmity)result;
		if (!me.isSuccess()) {
			return;			//原方法执行失败,不处理
		}
		
		add(joinPoint, me, sType);	//多线程处理
	}

	/**
	 * 添加数据
	 * @param joinPoint
	 * @param msgEmity
	 * @param sType
	 */
	private void add(JoinPoint joinPoint, MsgEmity msgEmity, String sType) {
		Object entity = msgEmity.getData();//执行了DAO的add、edit方法后,返回的实体对象，执行删除后返回编号
		String currentLoginUserId = ModuleUtil.currentLoginUserId();//取当前登录用户
		String tableName = ((MethodSignature) joinPoint.getSignature()).getDeclaringType().getSimpleName();
		Object[] methodArgs = joinPoint.getArgs();
		
		ModuleUtil.runThread(() -> {//多线程方式重新构造缓存数据
			if (null == entity) {
				return;
			}
			
			String msg = null;
			if (entity instanceof String || entity instanceof Integer || entity instanceof Long) {
				String tableNameThread = null == tableName ? "" : tableName;//xxServiceImpl
				int iEd = tableName.indexOf("ServiceImpl");
				iEd = -1 == iEd ? tableNameThread.length() : iEd;
				tableNameThread = tableNameThread.substring(0, iEd);
				
				StringBuilder sb = new StringBuilder().append("表:").append(tableNameThread).append(",参数:{");
				
				for (Object object : methodArgs) {
					sb.append(object).append(", ");
				}
				if (methodArgs.length > 0) {
					sb.setLength(sb.length() -2);
				}
				
				msg = sb.append("}").toString();
			} else {
				msg = entityToStr(entity);
			}
			
			msg = new StringBuilder(500).append(sType).append("数据[").append(msg).append("]").toString();
			
			SystemLog systemLog = new SystemLog();
			systemLog.setdLastTime(new Date());
			systemLog.setsAssistant(currentLoginUserId);
			systemLog.setiStatus(1);
			systemLog.setsMsg(msg);
			
			Integer iCount = systemLogMapper.add(systemLog);
			if (iCount != 1) {
				Log.error("操作信息添加系统日志失败!");
				return;
			}

			Log.debug("成功将操作信息添加系统日志");
		});
	}
	
	/**
	 * 信息对象转换字符串
	 * @param entity
	 * @return
	 */
	private static String entityToStr(Object entity) {
		if (entity instanceof String) {
			return (String)entity;
		}
		
		StringBuilder sb = new StringBuilder();

		Class<?> clazz = entity.getClass();
		while (clazz != null && !clazz.equals(Object.class)) {
			Field[] fields = clazz.getDeclaredFields();
			
			for (Field field : fields) {
				if ("/iVersion/iIndex/iState/dModifiedDate/sModifieder/dCreateDate/sCreator/sMemo/".contains(field.getName())) {
					continue;//不重要的信息不记录
				}

				boolean isAccessible = field.isAccessible();//保留属性是否可以访问权限
				field.setAccessible(true);//获取访问控制权限
				
				Object obj = null;
				try {
					obj = field.get(entity);
				} catch (Exception e) {
					field.setAccessible(isAccessible); // 恢复设置属性是否可以访问权限
					continue;//取值失败则下一个
				}
				
				if (null == obj) {
					field.setAccessible(isAccessible); // 恢复设置属性是否可以访问权限
					continue;
				}

				String str = field.getName();
				DataInfo dataInfo = field.getAnnotation(DataInfo.class);
				if (null != dataInfo) {
					str = dataInfo.sComment();//获取注解
				}
				
				sb.append(",").append(str).append(":").append(obj);
				
				field.setAccessible(isAccessible); // 恢复设置属性是否可以访问权限
			}
			
			clazz = clazz.getSuperclass();
		}
		
		if (sb.length() > 0) {
			sb.deleteCharAt(0);
		}
		
		return sb.toString();
	}
	
}
